//////////////////////////////////////////////////////////////////////   
// PowerThread.cpp: implementation of the CPowerThread class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "PowerThread.h"
#include "Msgqueue.h"


//--------------------------------------------------------------------
//Macro define
#define DEFAULT_TIMEOUT        1000//1000ms

//----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CPowerThread::CPowerThread():
m_bExitThread(TRUE),
m_ulWaitTime(DEFAULT_TIMEOUT),
m_bRunning(FALSE),
m_pNotifyPower(NULL)
{
    InitLock();
}

CPowerThread::~CPowerThread()
{
    DelLock();
}



//------------------------------------------------------------------
//Description:
//    Get the level of power from the PPOWER_BROADCAST struct
//
//Parameters:
//    pPowerInfo:[in] The struct includes the power information
//    piPercent:[out] The battery life percent.
//
//Return Values:
//    The power status
//----------------------------------------------------------------
PowerStatusType CPowerThread::GetPowerStatus(PPOWER_BROADCAST pPowerInfo, int *piPercent)
{
    PowerStatusType powStatus = POW_UNKNOW;

    if ( !pPowerInfo )
    {
        return POW_UNKNOW;
    }
    
    PPOWER_BROADCAST_POWER_INFO ppbpi = (PPOWER_BROADCAST_POWER_INFO) pPowerInfo->SystemPowerState;
    if ( !ppbpi )
    {
        return POW_UNKNOW;
    }
    
    *piPercent = ppbpi->bBatteryLifePercent; 
    
    if(ppbpi->bACLineStatus == AC_LINE_ONLINE)
    {        
        if(ppbpi->bBatteryFlag == BATTERY_FLAG_CHARGING)
        {
            //Charging
            powStatus = POW_CHARGING;
        }
        else
        {
            //May be full charging,or may be no battery
            powStatus = POW_CHARGEFULL;
        }
    }
    else
    {
        //Use battery
    
        if(0 <= ppbpi->bBatteryLifePercent && ppbpi->bBatteryLifePercent <= 20)
        {
            powStatus = POW_VLOW;
        }
        else if(20 < ppbpi->bBatteryLifePercent && ppbpi->bBatteryLifePercent <= 40)
        {
            powStatus = POW_LOW;
        }
        else if(40 < ppbpi->bBatteryLifePercent && ppbpi->bBatteryLifePercent <=60)
        {
            powStatus = POW_NORMAL;
        }
        else if(60 < ppbpi->bBatteryLifePercent && ppbpi->bBatteryLifePercent <=80)
        {
            powStatus = POW_HIGH;
        }
        else if(80 < ppbpi->bBatteryLifePercent && ppbpi->bBatteryLifePercent <= 100)
        {
            powStatus = POW_VHIGH;
        }
        else
        {
            powStatus = POW_UNKNOW;
        }
    }

    return powStatus;
}




//------------------------------------------------------------------
//Description:
//    Thread to get the power status
//----------------------------------------------------------------
DWORD WINAPI CPowerThread::PowerThread(PVOID pArg)
{
    CPowerThread *pObject = (CPowerThread *) pArg;

    pObject->m_bRunning = TRUE;

    BYTE pbMsgBuf[sizeof(POWER_BROADCAST) + sizeof(POWER_BROADCAST_POWER_INFO)];
    PPOWER_BROADCAST ppb = (PPOWER_BROADCAST) pbMsgBuf;
    MSGQUEUEOPTIONS msgopts;
    
    // Create our message queue
    memset(&msgopts, 0, sizeof(msgopts));
    msgopts.dwSize = sizeof(msgopts);
    msgopts.dwFlags = 0;
    msgopts.dwMaxMessages = 0;
    msgopts.cbMaxMessage = sizeof(pbMsgBuf);
    msgopts.bReadAccess = TRUE;
    
    HANDLE rghWaits[1] = { NULL };
    rghWaits[0] = CreateMsgQueue(NULL, &msgopts);
    if (!rghWaits[0])
    {
        //erro
        return 0x10;
    
    }
    
    HANDLE hReq = NULL;
    // Request notifications
    hReq = RequestPowerNotifications(rghWaits[0], PBT_POWERINFOCHANGE);
    if (!hReq)
    {
        CloseHandle( rghWaits[ 0 ] );
        //erro
        return 0x15;

    }      
    


    while(pObject->m_bExitThread == FALSE)
    {
        

        DWORD dwWaitCode = MsgWaitForMultipleObjectsEx( 1, rghWaits, pObject->m_ulWaitTime, QS_ALLINPUT, MWMO_INPUTAVAILABLE );
        if ( dwWaitCode == WAIT_OBJECT_0 )
        {              
            DWORD dwSize, dwFlags;
            BOOL bReadResult = ReadMsgQueue(rghWaits[0], ppb, sizeof(pbMsgBuf), &dwSize, 0, &dwFlags);
            if (bReadResult == TRUE)
            {
                int iPowPercent;
                PowerStatusType powStatus = pObject->GetPowerStatus(ppb,&iPowPercent);

                pObject->LockThis();
                if(pObject->m_pNotifyPower != NULL)
                {
                    pObject->m_pNotifyPower(powStatus,iPowPercent);
                }
                pObject->OnNotifyPower(powStatus,iPowPercent);
                pObject->UnLockThis();

            }
            else
            {
                // We should never get here
                break;                
            }
        }


    }
    pObject->m_bRunning = FALSE;
    
    return 0;
}




//------------------------------------------------------------------
//Description:
//    Set the timeout for the wait thread.  It is only for the MsgWaitForMultipleObjectsEx()
//The default value is DEFAULT_TIMEOUT
//----------------------------------------------------------------
void CPowerThread::SetTimeout(ULONG ulTime)
{
    m_ulWaitTime = ulTime;
}


//------------------------------------------------------------------
//Description:
//    Get the status of thread
//
//Return Values:
//    TRUE: The thread is running for capturing the power status.
//    FALSE: No thread running.
//----------------------------------------------------------------
BOOL CPowerThread::GetRunStatus()
{
    return m_bRunning;
}


//------------------------------------------------------------------
//Description:
//    start capturing the power status.If there is thread running,
//it will return FALSE;
//
//------------------------------------------------------------------
BOOL CPowerThread::StartCapture()
{
    if(m_bRunning == TRUE)
    {
        return FALSE;
    }

    m_bExitThread = FALSE;

    //Create the thread for batter sampled    
    DWORD dwPwrThdID;
    HANDLE hdThrd = CreateThread(NULL,0,PowerThread,(void *)this,0,&dwPwrThdID);
    if(hdThrd == NULL)
    {
        return FALSE;
    }
    CloseHandle(hdThrd);

    return TRUE;
}



//-----------------------------------------------------------------------------
//Description:
//    Stop capturing.
//
//--------------------------------------------------------------------------------
void CPowerThread::StopCapture()
{
    m_bExitThread = TRUE;
}


//------------------------------------------------------------------
//Description:
//    Set the callback function for receive the power status
//------------------------------------------------------------------
void CPowerThread::SetCallbackFunction(void (*pCallbackFunc)(PowerStatusType powStatus, int iBatteryPercent))
{
    LockThis();
    m_pNotifyPower = pCallbackFunc;
    UnLockThis();
}



//------------------------------------------------------------------
//Description:
//    Get the callback function
//------------------------------------------------------------------
void CPowerThread::GetCallbackFunction(void (* *pCallbackFunc)(PowerStatusType powStatus, int iBatteryPercent))
{
    LockThis();
    *pCallbackFunc = m_pNotifyPower;
    UnLockThis();
}


//------------------------------------------------------------------
//Description:
//    Resumn the sign order to get the current power status,or the power
//status will return untill the power status changed.
//------------------------------------------------------------------
BOOL CPowerThread::ResumnSign()
{
    /*
    BYTE pbMsgBuf[sizeof(POWER_BROADCAST) + sizeof(POWER_BROADCAST_POWER_INFO)];
    PPOWER_BROADCAST ppb = (PPOWER_BROADCAST) pbMsgBuf;
    MSGQUEUEOPTIONS msgopts;
    
    // Create our message queue
    memset(&msgopts, 0, sizeof(msgopts));
    msgopts.dwSize = sizeof(msgopts);
    msgopts.dwFlags = 0;
    msgopts.dwMaxMessages = 0;
    msgopts.cbMaxMessage = sizeof(pbMsgBuf);
    msgopts.bReadAccess = TRUE;
    
    HANDLE rghWaits[1] = { NULL };
    rghWaits[0] = CreateMsgQueue(NULL, &msgopts);
    if (!rghWaits[0])
    {
        //erro
        return FALSE;
    
    }
    

    // Request notifications
    HANDLE hReq = RequestPowerNotifications(rghWaits[0], PBT_POWERINFOCHANGE);
    if (!hReq)
    {
        CloseHandle( rghWaits[ 0 ] );
        //erro
        return FALSE;

    }      




    return TRUE;
    */


    return FALSE;
}



//------------------------------------------------------------------
//Description:
//    Notify the power. It's used as call back funtion.
//------------------------------------------------------------------
void CPowerThread::OnNotifyPower(PowerStatusType powStatus, int iBatteryPercent)
{
    return;
}